 package cheme.ProcessModel.Interpreter;

import java.beans.PropertyChangeEvent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;

import cheme.ProcessModel.AbstractParent;
import cheme.ProcessModel.ChemePropertyChangeEvent;
import cheme.ProcessModel.IChild;
import cheme.ProcessModel.IModelElement;
import cheme.ProcessModel.IPropertyAware;
import cheme.ProcessModel.ModelEnv;
import cheme.ProcessModel.ParsingUtil;
import cheme.ProcessModel.ProcessModel;
import cheme.ProcessModel.Equations.Equation;
import cheme.ProcessModel.Interpreter.IRule.RuleResult;
import cheme.ProcessModel.ProcessUnits.ProcessUnit;
import cheme.ProcessModel.ProcessUnits.Streams.Stream;
import cheme.ProcessModel.ProcessUnits.Streams.Tags.AbstractTagComponent;
import cheme.ProcessModel.ProcessUnits.Streams.Tags.IGeneralTagComponent;
import cheme.ProcessModel.ProcessUnits.Streams.Tags.ITagComponent;
import cheme.ProcessModel.ProcessUnits.Streams.Tags.OverallTagComponent;
import cheme.ProcessModel.ProcessUnits.Streams.Tags.Tag;
import cheme.ProcessModel.ProcessUnits.Streams.Tags.TagComponentList;
import cheme.ProcessModel.Table.Field;

public class PostParseRules
{

//	JB-Experiment
	public static final IRule processModelCheck = new IRule()
	{
		public cheme.ProcessModel.Interpreter.IRule.RuleResult applyTo(IModelElement object)
		{
			RuleResult result = new IRule.Warning((IModelElement)object, "This is custom message issues!!!");
			
			//ModelCheckCmd
			if(object instanceof ProcessModel)
			{
				ProcessModel pm = (ProcessModel)object;
				/*if(15 > pm.size())
				{
					Iterator<IChild> itr = pm.iterator();
					itr.next();// skip first element
					while(itr.hasNext())
					{
						ProcessUnit pu = (ProcessUnit) itr.next();
						// get streams
						Iterator<IChild> Streamitr = pu.getInStreams().iterator();
						
						while(Streamitr.hasNext())
						{
							Stream mu = (Stream)Streamitr.next();
							Iterator<Tag> StreamChilditr = mu.iterator();
							
							if(StreamChilditr!= null)
							{
								Tag tu = (Tag)StreamChilditr.next();							
								Iterator<IChild> Tagiterator = tu.iterator(); // For checking the type
								Iterator<IChild> Tempiterator = tu.iterator(); // For getting the value
								while(Tagiterator.hasNext())
								{
									if(Tagiterator.next() instanceof OverallTagComponent)
									{
										ITagComponent childComponent = (ITagComponent) Tempiterator.next();
										String strlabe = childComponent.getLabel();
										int pawan =0;
									}
									else
									{
										TagComponentList subComponent = (TagComponentList)Tempiterator.next();
										Iterator<IGeneralTagComponent> Listiterator = subComponent.getChildren().iterator();
										ITagComponent childComponent = null;
										while(Listiterator.hasNext())
										{				
											childComponent = (ITagComponent)Listiterator.next();
											String strlabel = childComponent.getLabel();
											int pawan = 0;
										}
									}
								}
							}
						}
						// Get out stream iterator
						//Iterator<IChild> StreamOutitr = pu.getOutStreams().iterator();
					}
					result = new IRule.Success((IModelElement)object, "Yay!!!");
				}*/
			}
			result = new IRule.Success((IModelElement)object, "");
			return result;
		}
	};
	
	// PA Experiment
	public static final IRule streamCheck = new IRule()
	{
		public cheme.ProcessModel.Interpreter.IRule.RuleResult applyTo(IModelElement object)
		{
			RuleResult result = new IRule.Warning((IModelElement)object, "!!! The Stream is in complete!!!");
			
			//ModelCheckCmd
			Stream st = (Stream)object;
			ProcessUnit outPU, inPU;
			
			outPU = st.getSink();
			inPU = st.getSource();
			
			if (null != outPU && null != inPU ){
				result = new IRule.Success((IModelElement)object, "");
			}
			
			
			return result;
		}
	};	
	// PA experiment- ends
	
	
	public static final IRule tagComponentUniqueID = new IRule()
	{
		public RuleResult applyTo(IModelElement object)
		{
//			RuleResult result = new IRule.Failure((IModelElement)object, "The element name is already in use.");
//			AbstractTagComponent component = (AbstractTagComponent)object;
//			
//			Interpreter interpreter = ModelEnv.getInstanceFor(component).getInterpreter();
//			
//			String label = component.getLabel();
//			ModelElementData modelData = interpreter.getModelData(label);
//			
//			System.out.println("Testing label: " + label + " for insertion to the table.");
//			if(null != modelData){
//				if(!interpreter.modelExists(label) || //name is new
//					component == modelData.getModel()) //name belongs to this element
//				{
//					result = new IRule.Success((IModelElement)object);
//				}
//			}
//		
//			
//			return result;
			
			return new IRule.Success((IModelElement)object);
		}
	};
	
	public static final IRule equationNonEmptyText = new IRule()
	{
		public RuleResult applyTo(IModelElement object)
		{
			RuleResult result = new IRule.Warning((IModelElement)object, "The element is empty!\n(Not very useful, now is it?)");
			Equation eq = (Equation)object;
			
			if(eq.isInitial())
			{
				result = new IRule.Success((IModelElement)object);
			}
			else
			{
				for(Field field : eq.getChildren())
				{
					if(!field.getText().equals(Equation.DEFAULT_VALUE))
					{
						result = new IRule.Success((IModelElement)object);
						break;
					}
				}
			}
			
			return result;
		}
	};
	
	public static final IRule tagComponentUnitNotSameAsOverAll = new IRule()
	{
		public RuleResult applyTo(IModelElement object)
		{
			RuleResult result = new IRule.Warning((IModelElement)object, "Units of the sub components has to be same as that of Overall Component)");
			AbstractTagComponent component = (AbstractTagComponent)object;		
			
			String parentUnit,currentUnit;		
				
			// Change is done to a Sub Component						
			OverallTagComponent tmpParent = (OverallTagComponent)((IModelElement)component.getParent().getParent().getChildren().get(0));
			parentUnit = tmpParent.getUnits();						
			currentUnit = component.getUnits();
			// If current unit is equal to overall unit 
				// OR
			// Current unit is % then success
			if((parentUnit.equals(currentUnit)) || currentUnit.equals("%"))
			{
				result = new IRule.Success((IModelElement)object);
			}					
			
			return result;
			
		}
		
		
	};
	
	// This rule will check for:
	// Incoming Overall Quantities are equal to outgoing overall qtys
	// Incoming Individual Quantities are equal to outgoing individual qtys
	public static final IRule Validatediagram = new ValidateDiagram()
	{
		@Override
		public RuleResult applyTo(IModelElement object)
		{			
			return super.applyTo(object);
		}

		
		

		
		
	};
	
	// This rule checks if the units and sum of qtys of all the individual elements are equal to overall qty and unit
	public static final IRule tagComponentUnitCheckAllChildren = new ComponentIteratingRule()
	{
		String OverallUnit = null;
		String lastUnit = null;
		float iOverAllQty = 0;
		Boolean bstatus = false;
		boolean bOverallIsUnknown = false;
		@Override
		public RuleResult applyTo(IModelElement object)
		{
			resetCommonUnit();
			return super.applyTo(object);
		}
		
		void resetCommonUnit()
		{
			OverallUnit = null;
			lastUnit = null;
			iOverAllQty = 0.0f;
		}
		
		void updateCommonUnit(String candidate)
		{
			if(null == OverallUnit)
			{
				OverallUnit = candidate;
			}
		}
						
		String getCommonUnit()
		{
			return OverallUnit;
		}
		
		// Set the last Unit of the component
		public void setlastUnit(String currentUnit)
		{
			if(null == lastUnit)
			{
				lastUnit = currentUnit;
			}
			
		}
		// check if the unit of current component is same as that of last unit
		// this check is to make sure that all are % or same unit as overall
		public Boolean checkUnit(String currentUnit)
		{
			bstatus = false;
			if (currentUnit.equals(lastUnit))
			{
				bstatus = true;			
			}
			return bstatus;
		}
		
		public void setOverallQty(String OverAllQty)
		{
			if( !(OverAllQty.isEmpty())&& !(OverAllQty.equals("?")))
				iOverAllQty = Float.valueOf(OverAllQty);
			else
				bOverallIsUnknown = true;
		}
		
		public float getOverallQty()
		{
			return iOverAllQty;
		}
		
		@Override
		protected RuleResult ComponentCheck(IModelElement object)
		{
			//RuleResult result = new IRule.Success((IModelElement)this.getComponent());			
			RuleResult result = new IRule.Warning((IModelElement)object, "Units associated with individual quantities do not match the units associated with the overall stream, or are not in %.\n Make sure units are consistent, or in %.");
			AbstractTagComponent component = (AbstractTagComponent)object;	
			if((component instanceof OverallTagComponent))
			{
				String parentUnit = component.getUnits();
				updateCommonUnit(parentUnit);
				setOverallQty(component.getQuantity());
				result = new IRule.Success((IModelElement)object);
			}
			else
			{
				String material = component.getMaterial();
				if(material.equalsIgnoreCase("Select")|| material.equals(""))
				{
					result = new IRule.Failure((IModelElement)object,"Component is not fully specified. Material field is required.");
				}
				else
				{
					String currentUnit =  component.getUnits();
					setlastUnit(currentUnit);
					if((currentUnit.equals(OverallUnit)) || currentUnit.equals("%"))
					{
						if( checkUnit(currentUnit))
						{
							result = new IRule.Success((IModelElement)object);
						}
					}
				}
			}
			
			return result;
		}
		
		protected boolean QtyCheck(float ichildQty)
		{
			boolean bStatus = false;
			// Overall Qty is unknown, so What are you checking against.
			// return true; No Message
			if(bOverallIsUnknown)
			{
				if(null != lastUnit && lastUnit.equals("%")){
					if(ichildQty == 100.0){
						bStatus = true;
					}
				}
				else{
					bStatus = true;
				}
				return bStatus;
			}
			if(null != lastUnit)
			{
				if(lastUnit.equals("%"))
				{
					if(ichildQty == 100.0)
						bStatus = true;
				}
				else
				{
					float i = getOverallQty();
					if(ichildQty == i)
					{
						bStatus = true;
					}
				}
				return bStatus;
			}
			
			return true;
		}
		
	};
	//PA Change - ends
	
	public static final IRule tagComponentNonEmptyText = new IRule()
	{
		public RuleResult applyTo(IModelElement object)
		{
			
			RuleResult result = new IRule.Warning((IModelElement)object, "The element is empty!\n(Not very useful, now is it?)");
			AbstractTagComponent component = (AbstractTagComponent)object;
									
			if(component.isInitial())
			{
				result = new IRule.Success((IModelElement)object);
			}
			else
			{
				for(Field field : component.getChildren())
				{
					if(!field.getText().equals(Equation.DEFAULT_VALUE))
					{
						result = new IRule.Success((IModelElement)object);
						break;
					}
				}
			}
			
			return result;
		}
	};
	
	/*
	 * Note that this will only be invoked when an alement isadded
	 */
//	private static IRule tagIsSpecified = new IRule()
//	{
//
//		public RuleResult check(Object object)
//		{
//			RuleResult result = null;
//			Tag tag = (Tag)object;
//			boolean isSPecified = isSpecified(tag.getOverallTag());
//			
//			if(null != tag.getTagComponentList())
//			{
//				Iterator<IGeneralTagComponent> itr = tag.getTagComponentList().getChildren().iterator();
//			
//				while(isSPecified && itr.hasNext())
//				{
//					AbstractTagComponent tagComponent = itr.next();
	//              if(!tagComponent.isInitial()){
//						isSPecified &= isSpecified();
//						 object = tagComponent;
//					}
//				}
//				
//				if(isSPecified)
//				{
//					result = new IRule.Success((IModelElement)object);
//				}
//				else
//				{
//					result = new IRule.Failure((IModelElement)object, "The stream is not sufficiently specified.");
//				}
//			}
//			
//			return result;
//		}
//		
//		private boolean isSpecified(ITagComponent component)
//		{
//			return !component.getLabel().isEmpty() && !component.getQuantity().isEmpty() && !component.getUnits().isEmpty();
//		}
//	};
	
	public static final IRule tagComponentIsSpecified = new IRule()
	{
		public RuleResult applyTo(IModelElement object)
		{
			RuleResult result = null;
			AbstractTagComponent tagComponent = (AbstractTagComponent)object;
			boolean isSPecified = isSpecified(tagComponent);
			
			if(isSPecified)
			{
				result = new IRule.Success((IModelElement)object);
			}
			else
			{
				result = new IRule.Failure((IModelElement)object, "The component is not sufficiently specified.");
			}
			
			return result;
		}
		
		private boolean isSpecified(ITagComponent component)
		{
			boolean bMaterial = component.getMaterial().equals("Select");// return false if material name is not specified
			return !component.getLabel().isEmpty() && !component.getUnits().isEmpty() && !bMaterial;
		}
	};
	
	public static final IRule tagReferrentsExist = new TagComponentQuantityIteratingRule()
	{
		@Override
		protected RuleResult tokenCheck(String quantityToken)
		{
			final String dummy = quantityToken;
			RuleResult result = new IRule.Success((IModelElement)this.getComponent());

			
			if(!quantityToken.isEmpty() && !ParsingUtil.isOperator(quantityToken) && !ParsingUtil.isNumber(quantityToken) && !interpreter.modelExists(quantityToken))
			{
				result = new IRule.Failure((IModelElement)getComponent(), "The stream component \"" + quantityToken + "\" does not exist.");
				
				/*
				 * If this fails, add a updater to refresh this whenever a new element is added to the 
				 * interpreter's lookuptable.
				 */
				
				IUpdater updater = null;
				
//				updater = new IUpdater.Stub(interpreter, getComponent())
//				{
//					@Override
//					public boolean allowPerformUpdate(PropertyChangeEvent evt)
//					{
//						return (((evt.getPropertyName().equals(IPropertyAware.P_ELEMENT_ADDED)) || 
//								(evt.getPropertyName().equals(IPropertyAware.P_ELEMENT_NAME_UPDATED))) && 
//								evt.getNewValue() != null && evt.getNewValue() instanceof IModelElement);
//					}
//					
//					@Override
//					public String toString()
//					{
//						return getComponent().getLabel() + " is listening for an addition of " + dummy + ".";
//					}
//
//					public void Stub(Interpreter interpreter, IModelElement toUpdate) {
//						// TODO Auto-generated method stub
//						
//					}
//				};
				
				//interpreter.addUpdater(updater);
			}

			return result;
		}
	};
	
		
	// Change this to IRUle and imlpement equation iteration with apply to
	// this was done due to fix an error in console message feedback mechanism.
	// THis rule was giving success and removing the annotation.
	public static final IRule equationReferrentsExist = new IterateEquations() //EquationIteratingRule()//
	{
		
		@Override
		protected RuleResult tokenCheck(Field equationToken)
		{
			RuleResult result = new IRule.Success((IModelElement)this.getEquation());
			final String token = equationToken.getText().trim();
					
			if(!token.isEmpty() && !ParsingUtil.isOperator(token) && !ParsingUtil.isNumber(token) && !interpreter.modelExists(token))
			{
				
				result = new IRule.Failure((IModelElement)getEquation(), "The variable named \"" + token + "\" has not been defined in the process flow diagram. \n Check to insure that every variable used in your equations is defined.");
				
				/*
				 * If this fails, add a updater to refresh this whenever a new element is added to the 
				 * interpreter's lookuptable.
				 */
				
				/*IUpdater updater = null;
				
				updater = new IUpdater.Stub(interpreter, getEquation())
				{
					@Override
					public boolean allowPerformUpdate(PropertyChangeEvent evt)
					{
						return (((evt.getPropertyName().equals(IPropertyAware.P_ELEMENT_ADDED)) || 
								(evt.getPropertyName().equals(IPropertyAware.P_ELEMENT_NAME_UPDATED))) && 
								evt.getNewValue() != null && evt.getNewValue() instanceof IModelElement);
					}

					@Override
					public String toString()
					{
						return "An equaiton is listening for the addition of " + token + ".";
					}
				};*/
				
				//interpreter.addUpdater(updater);
			}
			
			return result;
		}
	};
	
	// This rule checks that all the elements in the equation are of same type
	// Exceptions to this rule are:
	// Overall = sum of individual different elements
	// element = element(%)*Overall + elements
	public static final IRule equationIsCorrect = new EquationIteratingRule()
	{
		String commonMaterial = "";
		String lastMaterial = "";		
		String commonUnit = "";
		String lastUnit = "";		
		String lastOperator = "";
		private boolean bUpdateCommonUnit = true;
		private boolean bUpdateCommonMat = true;
		
		
		public RuleResult applyTo(IModelElement object)
		{
			resetCommon();
			return super.applyTo(object);
		}
		
		private void resetCommon() {
			// TODO Auto-generated method stub
			commonMaterial = "";
			commonUnit = "";
			lastMaterial = "";
			lastUnit = "";
			lastOperator = "";	
			bUpdateCommonUnit = true;
			bUpdateCommonMat = true;
		}
		@Override
		protected RuleResult tokenCheck(Field equationToken) {
			RuleResult result = new IRule.Failure(getEquation(), "Either you are referring to different components OR components with different units.");
			
			ModelElementData modelData = interpreter.getModelData(equationToken.getText());
			ITagComponent referencedTagComponent = null;
			String currentMat = "";
			String currentUnit = "";
			final String token = equationToken.getText().trim();
			
			if(token.isEmpty())
				{
					//result = new IRule.Failure(getEquation(), "Empty Token");
					return result;
				}
			
			if(ParsingUtil.isOperator(token)){
				lastOperator = token;
				// if = is passed then do not change the common material or common unit
				if (lastOperator.equals("="))
				{
					bUpdateCommonMat = false;
					bUpdateCommonUnit = false;
				}
				result = new IRule.Success((IModelElement)this.getEquation());
				return result;
			}
			// check for number
			if(ParsingUtil.isNumber(token)){
				result = new IRule.Success((IModelElement)this.getEquation());
				return result;
			}
			if(null != modelData && modelData.getModel() instanceof ITagComponent)
			{
				referencedTagComponent = (ITagComponent)modelData.getModel();
				currentMat = referencedTagComponent.getMaterial();
				currentUnit = referencedTagComponent.getUnits();
				
				if(!currentMat.isEmpty() && !ParsingUtil.isOperator(currentMat) && !ParsingUtil.isNumber(currentMat) && !interpreter.modelExists(currentMat)){
					updateCommonMat(currentMat);					
				}
				if(!currentUnit.isEmpty() && !ParsingUtil.isOperator(currentUnit) && !ParsingUtil.isNumber(currentUnit) && !interpreter.modelExists(currentUnit)){					
					updateCommonUnit(currentUnit);
				}				
				
				if(!lastOperator.isEmpty()&& !commonUnit.isEmpty()){
					// Case: % = % * % ( this is not allowed)
					/*if(lastOperator.equals("*")&& commonUnit.equals("%")){
						result = new IRule.Failure(getEquation(), "% = % * % or % = % * grams not allowed.");
						return result;
					}
					else if(lastOperator.equals("=")&& commonUnit.equals("%"))
					{
						result = new IRule.Failure(getEquation(), "% = % or % + % = %  not allowed.");
						lastUnit = currentUnit; // Copy the current unit into last unit
						lastMaterial = currentMat; // Copy the current mat into last mat
						return result;
					}*/
					if(commonUnit.equals("%"))
					{
						//result = new IRule.Failure(getEquation(), "Percentages from different streams cannot be added nor can percentages ever be multiplied.\n Check all of the terms in your balances to insure the proper use of percentages. Please correct your equation by making changes like % * OverAlll.");
						//Message changed after pilot study
						result = new IRule.Failure(getEquation(), "Improper use of percentages.\nCheck that % from different streams are not added, equated or multiplied by other %s");
						if(lastOperator.equals("=")){
							bUpdateCommonUnit  = false;
						}
						// update the common Unit with the current unit, "% * grams = grams"
						else if(!currentUnit.isEmpty() && !ParsingUtil.isOperator(currentUnit) && !ParsingUtil.isNumber(currentUnit) 
								&& !interpreter.modelExists(currentUnit) && lastOperator.equals("*") && bUpdateCommonUnit){					
							updateCommonUnit(currentUnit);
						}
						// if still common unit is % return the result
						if(commonUnit.equals("%"))
							return result;
					}
					
				}
				
				if(!currentMat.isEmpty() && !commonMaterial.isEmpty()){
					if(checkType(currentMat)){
						if(!(currentUnit.isEmpty()) && !(commonUnit.isEmpty())){
							if(!checkUnit(currentUnit)){
								// Case: m11 = M1(Overall) * m12(%)
								if(lastOperator.equals("*") && currentUnit.equals("%") && lastMaterial.equals("Overall") )
								{
									result = new IRule.Success((IModelElement)this.getEquation());
								}
								else{
									lastMaterial = currentMat;
									lastUnit = currentUnit; 
									result = new IRule.Failure(getEquation(), "All of the indiividual terms in a material balance must have the same units. \n Check the units on every term in this balance.");
								}
							}	
							
							else {
									if(!(lastOperator.equals("*"))){
										result = new IRule.Success((IModelElement)this.getEquation());
								}
									// m11 = m11 * M1
									else if(currentMat.equals("Overall")&& lastUnit.equals("%")){
										result = new IRule.Success((IModelElement)this.getEquation());
									}
							}
						}
						
					}
					// Elements are not same
					else{
						result = new IRule.Failure(getEquation(), "This material balance contains terms that involve more than one compound and is not an overall balance. \n Any material balance, other than the overall balalnce, must involve only a single chemical compound.");
						// Units should be same for spl cases
						if(checkUnit(currentUnit))
						{
							// Case: m11 = m12(%)* M1(Overall)
							// Also, ammonia(gm) = ammonia(%) * Overall(gms)
							// It was 'or' || in the last check. It changed to and to check that correct compund is multipled with the overall
							// To avoid the case mmonia(gm) = water(%) * Overall(gms)
							if(lastOperator.equals("*") && lastUnit.equals("%") && (currentMat.equals("Overall") && checkType(lastMaterial)) ) 
							{
								result = new IRule.Success((IModelElement)this.getEquation());
							}
													
						// if units are same but elements are not
						// Case: M1 = m11 + m12 + m13
						
							else if(commonMaterial.equals("Overall"))
							{
								result = new IRule.Success((IModelElement)this.getEquation());
							}						
						}
						// Case: Overall(gms) = Overall * % + Overall * %
						else if(commonMaterial.equals("Overall")&& lastOperator.equals("*")&& currentUnit.equals("%"))
						{
							result = new IRule.Success((IModelElement)this.getEquation());
						}	
						//else
						//{
							//result = new IRule.Failure(getEquation(), "The units on each term in a balance must be the same.\n Remember: The equation editior does not allow you to perform unit conversions. ");
						//}
						lastUnit = currentUnit; // Copy the current unit into last unit
						lastMaterial = currentMat; // Copy the current mat into last mat
					}
					/*if (!checkType(currentMat)){
						result = new IRule.Failure(getEquation(), "You can not operate on diffrent types of elements.This equation is invalid.\n Please correct your equation.");
					}*/
				}
			}
			
			return result;
		}

		private boolean checkUnit(String currentUnit) {
			if(currentUnit.equals(commonUnit))
			{
				//lastUnit = currentUnit; // Copy the current unit into last unit
				return true;
			}
			return false;
		}

		private boolean checkType(String currentMat) {
			if(currentMat.equals(commonMaterial))
			{
				//lastMaterial = currentMat; // Copy the current mat into last mat
				return true;
			}
			
			return false;
		}

		private void updateCommonMat(String currentMat) {
			if(commonMaterial.isEmpty())
			{
				commonMaterial = currentMat;
			}
			else if (bUpdateCommonMat && commonMaterial.equals("Overall") && !(currentMat.isEmpty()))
			{
				commonMaterial = currentMat;
			}
		}
		
		private void updateCommonUnit(String currentUnit) {
			if(commonUnit.isEmpty())
			{
				commonUnit = currentUnit;
			}
			// e.g. % * grams, common unit is grams
			else if(commonUnit.equals("%")&& !currentUnit.isEmpty())
			{
				lastUnit = commonUnit;
				commonUnit = currentUnit;
			}
		}
		
	};
	
	public static final IRule unitAgreementEx = new EquationIteratingRule()
	{
		String commonUnit = null;
		String lastUnit = null; // PA Change
		String lastOperator = null; // PA Change
		@Override
		public RuleResult applyTo(IModelElement object)
		{
			resetCommonUnit();
			return super.applyTo(object);
		}
		
		void resetCommonUnit()
		{
			commonUnit = null;
		}
		
		void updateCommonUnit(String candidate)
		{
			if(null == commonUnit)
			{
				commonUnit = candidate;
			}
		}
		
		// PA Change
		void updateLastUnit(String candidate){
			lastUnit = candidate;
		}
		
		void updateLastOperator(String candidate){
			lastOperator = candidate;
		}
		// PA Change ends
		
		String getCommonUnit()
		{
			return commonUnit;
		}
		
		@Override
		protected RuleResult tokenCheck(Field equationToken)
		{
			RuleResult result = new IRule.Success((IModelElement)this.getEquation());
			
			ModelElementData modelData = interpreter.getModelData(equationToken.getText());
			ITagComponent referencedTagComponent = null;
			String currentUnit = null;
			final String operatorText =  equationToken.getText().trim();
			
			if(operatorText.isEmpty())
			{
				return result;//&& !ParsingUtil.isOperator(token) && !ParsingUtil.isNumber(token) && !interpreter.modelExists(token))
			}
			// Pa Change
			// Check if last unit was % and it is not followed by "*" operator then return failure.
			// for % unit it has to be multiplied with other valid unit.
			// e.g grams = % * grams, % = % * grams
			if( (lastUnit != null) &&  lastUnit.equals("%")){
				result = new IRule.Failure(getEquation(), "You are referring to stream elements with different units of measurement.");
				
				if(ParsingUtil.isOperator(operatorText) ){
					if(operatorText.equals("*") || operatorText.equals("="))
						result = new IRule.Success((IModelElement)this.getEquation());
					
					lastUnit = null;
				}				
			}
			
			// store last operator if it is *			
			if((operatorText.equals("*"))){
				updateLastOperator(operatorText);
			}
			// Pa Change ends	
			
			if(null != modelData && modelData.getModel() instanceof ITagComponent)
			{
				referencedTagComponent = (ITagComponent)modelData.getModel();
				currentUnit = referencedTagComponent.getUnits();
				updateCommonUnit(currentUnit);
				
				// Pa Change
				// current unit is % and last operator is not *
				if(currentUnit.trim().equals("%") && null == lastOperator){
					updateLastUnit(currentUnit);
				}
				// Pa Change - ends
				
				if(!getCommonUnit().equals(currentUnit))
				{
					//Pa Change
					// check if last unit is not % but last operator was "*" and current unit is % then do not return failure.
					// e.g m11 = grams * % ---> correct equation
					if(null != lastOperator){
						if(!(currentUnit.trim().equals("%"))){
							result = new IRule.Failure(getEquation(), "You are referring to stream elements with different units of measurement.");							
						}
						lastOperator = null;
					}
					//Pa Change ends
					// Only "else" added by PA
					else{ 
						result = new IRule.Failure(getEquation(), "You are referring to stream elements with different units of measurement.");
					}
					/*
					 * If this fails, add a updater to refresh this whenever a new element is added to the 
					 * interpreter's lookuptable.
					 */
					
					/*IUpdater updater = new IUpdater.Stub(interpreter, getEquation())
					{
						@Override
						public boolean allowPerformUpdate(PropertyChangeEvent evt)
						{
							ChemePropertyChangeEvent chemeEvt = (ChemePropertyChangeEvent)evt;
							return (chemeEvt.includesEvent(IPropertyAware.P_UNITS));
						}
						
						@Override
						public String toString()
						{
							return "An equaiton is listening for an unit";
						}
					};*/
					
					//interpreter.addUpdater(updater);
				}
				// Pa - change starts
				// Reset the last operator if it was * and there is no conflict in the Units.
				else{
					if(null != lastOperator){
						lastOperator = null;
					}
				}
				// Pa - change ends
			}
			
			return result;
		}
	};
	
	public static final IRule tagComponentRefsNotCircular = new IRule()
	{
		/*
		 * Poor man's enum since java does not appear to like nested enums in anonymous classes
		 */

			private final int CIRCULAR = 0;
			private final int NON_CIRCULAR = 1;
				
		protected Interpreter interpreter = null;
		
		
		public RuleResult applyTo(IModelElement object)
		{
			RuleResult result = new IRule.Failure((IModelElement)object, "One of your variables refers to itselfs, perhaps indirectly by referring to another variables's value.");
			
			ITagComponent component = null;
			
			if(object instanceof ITagComponent)
			{
				component = (ITagComponent)object;
				interpreter = ModelEnv.getInstanceFor(component).getInterpreter();
				
				String quantity = component.getQuantity();
				
				if(NON_CIRCULAR == checkRefs(quantity))
				{
					result = new IRule.Success((IModelElement)object);
				}
			}
			
			return result;
		}
		
		protected int checkRefs(String quantity)
		{
			return checkRefs(quantity, new HashSet<String>());
		}
		
		protected int checkRefs(String quantity, Set<String> referencesSet)
		{
			int result = NON_CIRCULAR;
			
			String token = "";
			StringTokenizer tokenizer = new StringTokenizer(quantity, " \t+-()*^/\\", true); //true entails delimters are returned as tokens
			
			boolean isNumber = false;
			
			ModelElementData modelData = null;
			ITagComponent referencedTagComponent = null;
			String localQuantity = null;

			//for each token in the quantity
			while(tokenizer.hasMoreTokens())
			{
				token = tokenizer.nextToken().trim();
				
				//perform number test
				try
				{
					Double.parseDouble(token); //added the constraint that the string parse to a numeric value.
					
					//if the candidate is a valid numeric string
					isNumber = true;
				}
				catch(Exception e)
				{
					isNumber = false;
				}
				
				//if the candidate is a valid numeric string, nothing special to do
				
				if(false == isNumber) //if it is a ref to another component
				{
					//it's a reference, so add it to the set and make sure that it is unique
					if(referencesSet.add(token))
					{
						//if it adds successfully, check the tagComponents that it references
						modelData = interpreter.getModelData(token);
						
						if(null != modelData && modelData.getModel() instanceof ITagComponent)
						{
							referencedTagComponent = (ITagComponent)modelData.getModel();
							localQuantity = referencedTagComponent.getQuantity();
							
							checkRefs(localQuantity, referencesSet);
						}
					}
					else
					{
						result = CIRCULAR;
					}
				}
			}
			
			return result;
		}
	};
	
	public static final IRule tagSumsToLessThanOne = new ParentIteratingRule()
	{
		@Override
		protected RuleResult tokenCheck(IChild child)
		{
			RuleResult result = new IRule.Success((IModelElement)this.getObject());
			
			ITagComponent component = null;
			
			if(null != child && child instanceof ITagComponent)
			{
				component = (ITagComponent)child;
			}
			
			String quantityString = component.getQuantity();
			double sum = sumQuantity(quantityString);
			
			if(1.0 < sum)
			{
				result = new IRule.Failure((IModelElement)this.getObject(), "This stream tag sums to more than 100%.");
			}
			
			return result;
		}
		
		protected double sumQuantity(String quantity)
		{
			double sum = 0.0; 	//In this the sum of values + refs is accumulated.

			String token = "";
			StringTokenizer tokenizer = new StringTokenizer(quantity, " \t+-()*^/\\", true); //true entails delimters are returned as tokens
			
			boolean isNumber = false;
			double numericValue = 0.0;
			
			ModelElementData modelData = null;
			ITagComponent referencedTagComponent = null;
			String quantityString = null;

			//for each token in the quantity
			while(tokenizer.hasMoreTokens())
			{
				token = tokenizer.nextToken().trim();
				
				//perform number test
				try
				{
					numericValue = Double.parseDouble(token); //added the constraint that the string parse to a numeric value.
					
					//if the candidate is a valid numeric string
					isNumber = true;
				}
				catch(Exception e)
				{
					isNumber = false;
				}
				
				//if the candidate is a valid numeric string, nothing special to do
				
				if(!isNumber) //if it is a ref to another component
				{
					modelData = interpreter.getModelData(token);
					
					if(null != modelData && modelData.getModel() instanceof ITagComponent)
					{
						referencedTagComponent = (ITagComponent)modelData.getModel();
						quantityString = referencedTagComponent.getQuantity();
					}
					
					//sum that component
					numericValue = sumQuantity(quantityString);
				}
				
				//add it to the sum
				sum += numericValue;
			}
			
			return sum;
		}
	};

	public static final IRule equationIndependence =  new EquationIndependenceRule()
	{		
		@Override
		public RuleResult applyTo(IModelElement object)
		{
			return super.applyTo(object);
		}

		@Override
		//protected boolean EquationIndependenceCheck(int iUnknownCount, int iLinear, int iQudratic) {
		protected boolean EquationIndependenceCheck(int iUnknownCount, int iEquationCount) {
			// TODO Auto-generated method stub
			boolean result = false;
			
			if(iUnknownCount <= iEquationCount )
			{
				result = true;
			}
			return result;
		}				
	};
	
	
}
